package com.yc.cloud.portal.service.impl;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpResponse;
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.yc.cloud.common.basic.api.ResponseResult;
import com.yc.cloud.common.basic.exception.Asserts;
import com.yc.cloud.common.websocket.core.SocketServer;
import com.yc.cloud.common.websocket.dto.SingleMessageDto;
import com.yc.cloud.common.websocket.listener.PlayerStatusListener;
import com.yc.cloud.common.websocket.vo.PlayerVo;
import com.yc.cloud.portal.common.PortalErrorTips;
import com.yc.cloud.portal.request.ARControlParam;
import com.yc.cloud.portal.request.AROCRParam;
import com.yc.cloud.portal.request.ARRTCCallbackParam;
import com.yc.cloud.portal.request.MediaBasicInfoParam;
import com.yc.cloud.portal.service.ARPathService;
import com.yc.cloud.portal.service.ARService;
import com.yc.cloud.portal.service.MediaService;
import com.yc.cloud.portal.vo.OCRResultVo;
import jakarta.annotation.PreDestroy;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import lombok.val;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

@Service
@Slf4j
public class ARServiceImpl implements ARService, PlayerStatusListener {

    private final SocketServer socketServer;


    private final MediaService mediaService;

    private final ARPathService arPathService;

    @Value("${ocr.url:http://localhost:5000/ocr}")
    private String OCR_URL;


    public ARServiceImpl(SocketServer socketServer, MediaService mediaService, ARPathService arPathService) {
        this.socketServer = socketServer;
        this.mediaService = mediaService;
        this.arPathService = arPathService;
//        this.arDeviceStatusService = arDeviceStatusService;
        socketServer.registerUserStatusListener(this);
    }

    @PreDestroy
    public void destroy() {
        socketServer.unregisterUserStatusListener(this);
    }

    @Override
    public boolean rtcCallBack(ARRTCCallbackParam param) {
        val jsonObject = new JSONObject();
        jsonObject.putOpt("channelCode", param.getChannelCode());
        jsonObject.putOpt("streamUrls", param.getStreamUrls());
        jsonObject.putOpt("eventType", param.getEventType());
        // 发送消息到指定设备
        val receiver = param.getDeviceNum();
        String OPERATION_TYPE_RTC_CALLBACK = "rtc_callback";
        return sendSingleMessage(jsonObject.toString(), receiver, OPERATION_TYPE_RTC_CALLBACK);
    }

    @Override
    public boolean controlARDevice(ARControlParam param) {
        return sendSingleMessage(param.getContent(), param.getDeviceNum(), param.getOperationType());
    }

    @Override
    @SneakyThrows
    public OCRResultVo ocr(AROCRParam param, MultipartFile file) {
        val fileName = System.currentTimeMillis() + "." + FileUtil.getSuffix(file.getOriginalFilename());
        //保存到对应设备编号的OCR文件夹
        val ocrFile = arPathService.getOcrFilePath(param.getDeviceNum(), fileName);
        FileUtil.writeFromStream(file.getInputStream(), ocrFile);
        // 调用OCR接口，返回结果
        try (HttpResponse response = HttpUtil.createPost(OCR_URL)
                .form("file", ocrFile) // 以表单的形式上传文件
                .execute()) {
            val result = response.body();
            log.info("OCR接口返回结果：{}", result);
            if (StrUtil.isEmpty(result)) {
                Asserts.fail(PortalErrorTips.OCR_API_ERROR);
            }
            val ocrResult = JSONUtil.toBean(result, ResponseResult.class);
            if (ocrResult.getCode() != 200) {
                Asserts.fail(ocrResult.getMessage());
            }
            val ocrData = ocrResult.getData();
            val vo = new OCRResultVo();
            vo.setOcrInfo(JSONUtil.toJsonStr(ocrData));
            vo.setOcrFileName(fileName);
            return vo;
        } catch (Exception e) {
            val errorMsg = PortalErrorTips.OCR_API_ERROR + ",错误信息：" + e.getMessage();
            log.error(errorMsg);
            Asserts.fail(errorMsg);
            return null;
        }
    }


    /**
     * 发送单条消息
     *
     * @param content       消息内容
     * @param receiver      接收者
     * @param operationType 操作类型
     * @return 是否发送成功
     */
    private boolean sendSingleMessage(String content, String receiver, String operationType) {
        val playerVo = socketServer.getOnLinePlayer(receiver);
        if (playerVo == null) {
            Asserts.fail(PortalErrorTips.DEVICE_NOT_ONLINE);
        }
        val singleMessage = new SingleMessageDto();
        singleMessage.setContent(content);
        singleMessage.setReceiver(receiver);
        String PROJECT_NAME = "custom_ar";
        singleMessage.setSender(PROJECT_NAME);
        singleMessage.setOperationType(operationType);
        val sendResult = socketServer.sendSingleMessage(singleMessage);
        if (sendResult.getCode() != 200) {
            val errorMsg = "发送消息失败：receiver=" + receiver + ",operationType=" + operationType + ",message=" + sendResult.getMsg();
            log.error(errorMsg);
            Asserts.fail(sendResult.getMsg());
        }
        return true;
    }

    /**
     * 有设备连接到websocket后,回调该方法,上层自己做业务处理,比如上报设备状态
     *
     * @param player 设备信息
     */
    @Override
    public void onPlayerConnected(PlayerVo player) {
        // 连接成功后，向该设备发送消息
        val basicInfoParam = new MediaBasicInfoParam(player.getDisplay(), "AR", "on");
        uploadDeviceState(basicInfoParam);
    }

    /**
     * 有设备断开=websocket后,回调该方法,上层自己做业务处理,比如上报设备状态
     *
     * @param player 设备信息
     */
    @Override
    public void onPlayerDisconnected(PlayerVo player) {
        val basicInfoParam = new MediaBasicInfoParam(player.getDisplay(), "AR", "off");
        uploadDeviceState(basicInfoParam);
    }

    private void uploadDeviceState(MediaBasicInfoParam param) {
        val postDeviceStatusResult = mediaService.basicInfo(param);
        val deviceNum = param.getDeviceNum();
        val state = param.getState();
        if (postDeviceStatusResult.getCode() != 200) {
            log.error("上报设备状态失败：deviceNum={}, status={}, message={}", deviceNum, state, postDeviceStatusResult.getMessage());
            return;
        }
        log.info("上报设备状态成功：deviceNum={}, status={}", deviceNum, state);
    }
}
